The goal of this analysis is to determine what name is in primary use
at this stage for DOID:6457. Multiple terms exist that appear to be
equivalent (see issue #1514)
and there are numerous names for this disease.
data_dir <- here::here("data/disease_info")
data_file <- file.path(data_dir, "onyong-nyong-fever.rda")
if (!dir.exists(data_dir)) dir.create(data_dir, recursive = TRUE)
# get_ftxt_safely() will automatically get PMC articles or books and will NOT
# fail if on errors caused by individual download failures
safe_epmc_ftxt <- purrr::safely(europepmc::epmc_ftxt, otherwise = NA, quiet = FALSE)
safe_epmc_ftxt_bk <- purrr::safely(europepmc::epmc_ftxt_book, otherwise = NA, quiet = FALSE)
get_ftxt_safely <- function(pmcid = NA, bookid = NA) {
if (is.na(pmcid) && is.na(bookid) ) return(NA)
if (!is.na(pmcid)) {
out <- list(safe_epmc_ftxt(pmcid))
} else {
out <- list(safe_epmc_ftxt_bk(bookid))
}
cat(".")
out
}
# parse_ftxt_xml() parses results from get_ftxt_safely()
parse_ftxt_xml <- function(safe_ftxt_xml, xml_accessor) {
if (!is.null(safe_ftxt_xml$error)) {
return(paste0("ERROR: ", safe_ftxt_xml$error$message))
}
out <- safe_ftxt_xml$result |>
xml2::xml_find_all(xml_accessor) |>
xml2::xml_text()
if (length(out) == 0) {
out <- paste0(
"ERROR [NO BODY]: ",
xml2::xml_text(safe_ftxt_xml$result)
)
if (length(out) == 0) {
out <- "ERROR: No text extractable"
}
} else if (length(out) > 1) {
out <- DO.utils::vctr_to_string(out, delim = "%%%%%") |>
paste0("WARNING: Multilength output, separated by %%%%%.")
}
out
}
These are the terms currently in DO, or that have been identified in
initial searches.
terms <- c(
"O'nyong'nyong fever", # current label
"O'nyong-nyong",
"o'nyong-nyong"
)
Begin by searching EuropePMC for articles that contain one or more
exact matches to these terms using the default search. Save output
to file, to avoid potential of repeat API call.
# exclude abbreviations when searching for publications (too likely to
search_str <- paste0(
'OPEN_ACCESS:y AND (',
paste0('"', terms[stringr::str_length(terms) > 4] , '"', collapse = " OR "),
')'
)
if (!file.exists(data_file)) {
res <- europepmc::epmc_search(search_str, synonym = FALSE, limit = 20000)
save(res, file = data_file)
} else {
load(data_file)
}
The number of publication hits (NULL) can be reasonably be processed
using all the full text articles, excluding preprints and
retractions.
res_tidy <- res |>
dplyr::filter(!stringr::str_detect(pubType, "retract|preprint")) |>
dplyr::select(
"id", "title", "pubYear", pubDate = "firstPublicationDate", "pmcid"
) |>
dplyr::mutate(
pubDate = lubridate::as_date(pubDate),
pubYear = lubridate::year(pubDate)
)
if (!exists("res_ftxt")) {
res_ftxt <- res_tidy |>
dplyr::rowwise() |>
dplyr::mutate(ft_xml = get_ftxt_safely(pmcid)) |>
dplyr::mutate(ft = parse_ftxt_xml(ft_xml, "//body"))
save(res, res_ftxt, file = data_file)
}
Evaluating Usage
Extracting all these values from the full text of the sample
publications and all the titles (in a case-insensitive manner).
regex_str <- "o.nyong.nyong( fever)?"
term_df <- res_tidy |>
dplyr::left_join(
res_ftxt,
by = c("id", "title", "pubYear", "pubDate", "pmcid")
) |>
dplyr::select("id", "pubDate", "title", "ft") |>
dplyr::mutate(
title_match = stringr::str_extract_all(
.data$title,
stringr::regex(regex_str, ignore_case = TRUE)
),
ft_match = stringr::str_extract_all(
.data$ft,
stringr::regex(regex_str, ignore_case = TRUE)
)
) |>
tidyr::unnest(title_match, keep_empty = TRUE) |>
tidyr::unnest(ft_match, keep_empty = TRUE) |>
dplyr::mutate(ft = !is.na(ft))
The number of publications with and without matches in their titles
or full text, noting whether their full-text was obtained are as
follows:
term_df |>
dplyr::summarize(
title_match = any(!is.na(title_match)),
ft_match = any(!is.na(ft_match)),
ft = unique(ft),
.by = "id"
) |>
dplyr::count(ft, title_match, ft_match) |>
dplyr::mutate(pct = round(n / sum(n) * 100, 2)) |>
dplyr::rename(ft_obtained = "ft")
Any non-matches will just be dropped for the analysis of names, and
special quote or dash marks will be standardized to '.
matches <- term_df |>
tidyr::pivot_longer(
title_match:ft_match,
names_to = c("source", ".value"),
names_sep = "_",
values_drop_na = TRUE
) |>
dplyr::mutate(
match = stringr::str_replace_all(
.data$match,
c("['‘’′]" = "'", "[-‐–]" = "-")
),
match_lc = stringr::str_to_lower(.data$match)
) |>
dplyr::select(-"title")
The number of case-insenitive matches in the titles and full text are
as follows:
matches |>
DO.utils::collapse_col("match") |>
dplyr::count(.data$source, .data$match_lc)
The number of matches, in the full text only, preserving case are as
follows:
ft_matches <- matches |>
dplyr::filter(.data$source == "ft")
ft_matches |>
DO.utils::collapse_col("match_lc") |>
dplyr::count(.data$match, sort = TRUE)
The current name in DO is pretty low in the list and doesn’t match
the original. The top two are the original, with the second being the
original capitalization. The uppercase version is much more common. It’s
much less common to find the name with “fever” but that’s probably to be
expected since the name of the virus will almost certainly be used at
least once with each disease reference, and often much more.
Organized by publication date and binned into year intervals (limited
to the top ten), the results are as follows:
g_colors <- hues::iwanthue(dplyr::n_distinct(ft_matches$match))
ft_matches |>
DO.utils::collapse_col("match_lc") |>
dplyr::mutate(n = length(.data$source), .by = "match") |>
ggplot2::ggplot() +
ggplot2::geom_freqpoly(
ggplot2::aes(x = pubDate, color = match),
binwidth = 365
) +
ggplot2::scale_color_manual(values = g_colors) +
ggplot2::facet_wrap(~ source, ncol = 1, scales = "free_y")

Hmm… the oldest uses are quite a long time ago and make the graph a
bit hard to read. Subsetting the graph to after the year 2000:
g <- ft_matches |>
DO.utils::collapse_col("match_lc") |>
dplyr::mutate(n = length(.data$source), .by = "match") |>
ggplot2::ggplot() +
ggplot2::geom_freqpoly(
ggplot2::aes(x = pubDate, color = match, group = match),
binwidth = 365,
linewidth = 1
) +
ggplot2::scale_color_manual(values = g_colors) +
ggplot2::scale_x_date(
date_breaks = "5 years",
date_labels = "%Y"
) +
ggplot2::facet_wrap(~ source, ncol = 1, scales = "free_y") +
ggplot2::coord_cartesian(
xlim = c(as.Date("2000-01-01"), as.Date("2025-01-01"))
) +
ggplot2::theme_minimal()
plotly::ggplotly(g)
Based on this, it’s very clear that the capitalized version of the
original name is most common. Just looking at the disease name just to
be sure this is also the case for the disease:
g <- ft_matches |>
dplyr::filter(stringr::str_detect(.data$match, "ever")) |>
DO.utils::collapse_col("match_lc") |>
dplyr::mutate(n = length(.data$source), .by = "match") |>
ggplot2::ggplot() +
ggplot2::geom_freqpoly(
ggplot2::aes(x = pubDate, color = match, group = match),
binwidth = 365,
linewidth = 1
) +
ggplot2::scale_color_manual(values = g_colors) +
ggplot2::scale_x_date(
date_breaks = "5 years",
date_labels = "%Y"
) +
ggplot2::facet_wrap(~ source, ncol = 1, scales = "free_y") +
ggplot2::theme_minimal()
plotly::ggplotly(g)
A general sum would be more useful but the differences there are
negligible. In this case, it makes sense to follow the most common name
for the virus.
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgRE9JRDo2NDU3IG5hbWUgJiBzeW5vbnltcyIKZGF0ZTogIjIwMjUtMDItMjEiCm91dHB1dDoKICAgIGh0bWxfbm90ZWJvb2s6CiAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBhbmFseXNpcyBpcyB0byBkZXRlcm1pbmUgd2hhdCBuYW1lIGlzIGluIHByaW1hcnkgdXNlIGF0IHRoaXMgc3RhZ2UgZm9yIERPSUQ6NjQ1Ny4gTXVsdGlwbGUgdGVybXMgZXhpc3QgdGhhdCBhcHBlYXIgdG8gYmUgZXF1aXZhbGVudCAoc2VlIGlzc3VlIFsjMTUxNF0oaHR0cHM6Ly9naXRodWIuY29tL0Rpc2Vhc2VPbnRvbG9neS9IdW1hbkRpc2Vhc2VPbnRvbG9neS9pc3N1ZXMvMTUxNCkpIGFuZCB0aGVyZSBhcmUgbnVtZXJvdXMgbmFtZXMgZm9yIHRoaXMgZGlzZWFzZS4KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShldXJvcGVwbWMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHhtbDIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShodWVzKQpsaWJyYXJ5KHBsb3RseSkKYGBgCgpgYGB7ciBpbl9wcm9ncmVzc19kYXRhfQpkYXRhX2RpciA8LSBoZXJlOjpoZXJlKCJkYXRhL2Rpc2Vhc2VfaW5mbyIpCmRhdGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJjb3dkZW5fc3luZHJvbWVfMS5yZGEiKQoKaWYgKCFkaXIuZXhpc3RzKGRhdGFfZGlyKSkgZGlyLmNyZWF0ZShkYXRhX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgpgYGB7ciBjdXN0b21fZnVuY3Rpb25zfQojIGdldF9mdHh0X3NhZmVseSgpIHdpbGwgYXV0b21hdGljYWxseSBnZXQgUE1DIGFydGljbGVzIG9yIGJvb2tzIGFuZCB3aWxsIE5PVAojICAgZmFpbCBpZiBvbiBlcnJvcnMgY2F1c2VkIGJ5IGluZGl2aWR1YWwgZG93bmxvYWQgZmFpbHVyZXMKc2FmZV9lcG1jX2Z0eHQgPC0gcHVycnI6OnNhZmVseShldXJvcGVwbWM6OmVwbWNfZnR4dCwgb3RoZXJ3aXNlID0gTkEsIHF1aWV0ID0gRkFMU0UpCnNhZmVfZXBtY19mdHh0X2JrIDwtIHB1cnJyOjpzYWZlbHkoZXVyb3BlcG1jOjplcG1jX2Z0eHRfYm9vaywgb3RoZXJ3aXNlID0gTkEsIHF1aWV0ID0gRkFMU0UpCgpnZXRfZnR4dF9zYWZlbHkgPC0gZnVuY3Rpb24ocG1jaWQgPSBOQSwgYm9va2lkID0gTkEpIHsKICAgIGlmIChpcy5uYShwbWNpZCkgJiYgaXMubmEoYm9va2lkKSApIHJldHVybihOQSkKICAgIGlmICghaXMubmEocG1jaWQpKSB7CiAgICAgICAgb3V0IDwtIGxpc3Qoc2FmZV9lcG1jX2Z0eHQocG1jaWQpKQogICAgfSBlbHNlIHsKICAgICAgICBvdXQgPC0gbGlzdChzYWZlX2VwbWNfZnR4dF9iayhib29raWQpKQogICAgfQogICAgY2F0KCIuIikKICAgIG91dAp9CgoKIyBwYXJzZV9mdHh0X3htbCgpIHBhcnNlcyByZXN1bHRzIGZyb20gZ2V0X2Z0eHRfc2FmZWx5KCkKcGFyc2VfZnR4dF94bWwgPC0gZnVuY3Rpb24oc2FmZV9mdHh0X3htbCwgeG1sX2FjY2Vzc29yKSB7CiAgICBpZiAoIWlzLm51bGwoc2FmZV9mdHh0X3htbCRlcnJvcikpIHsKICAgICAgICByZXR1cm4ocGFzdGUwKCJFUlJPUjogIiwgc2FmZV9mdHh0X3htbCRlcnJvciRtZXNzYWdlKSkKICAgIH0KICAgIG91dCA8LSBzYWZlX2Z0eHRfeG1sJHJlc3VsdCB8PgogICAgICAgIHhtbDI6OnhtbF9maW5kX2FsbCh4bWxfYWNjZXNzb3IpIHw+CiAgICAgICAgeG1sMjo6eG1sX3RleHQoKQoKICAgIGlmIChsZW5ndGgob3V0KSA9PSAwKSB7CiAgICAgICAgb3V0IDwtIHBhc3RlMCgKICAgICAgICAgICAgIkVSUk9SIFtOTyBCT0RZXTogIiwKICAgICAgICAgICAgeG1sMjo6eG1sX3RleHQoc2FmZV9mdHh0X3htbCRyZXN1bHQpCiAgICAgICAgKQogICAgICAgIGlmIChsZW5ndGgob3V0KSA9PSAwKSB7CiAgICAgICAgICAgIG91dCA8LSAiRVJST1I6IE5vIHRleHQgZXh0cmFjdGFibGUiCiAgICAgICAgfQogICAgfSBlbHNlIGlmIChsZW5ndGgob3V0KSA+IDEpIHsKICAgICAgICBvdXQgPC0gRE8udXRpbHM6OnZjdHJfdG9fc3RyaW5nKG91dCwgZGVsaW0gPSAiJSUlJSUiKSB8PgogICAgICAgICAgICBwYXN0ZTAoIldBUk5JTkc6IE11bHRpbGVuZ3RoIG91dHB1dCwgc2VwYXJhdGVkIGJ5ICUlJSUlLiIpCiAgICB9CgogICAgb3V0Cn0KYGBgCgoKVGhlc2UgYXJlIHRoZSB0ZXJtcyBjdXJyZW50bHkgaW4gRE8sIG9yIHRoYXQgaGF2ZSBiZWVuIGlkZW50aWZpZWQgaW4gaW5pdGlhbCBzZWFyY2hlcy4KCmBgYHtyfQp0ZXJtcyA8LSBjKAogICAgIlBURU4gaGFtYXJ0b21hIHR1bW9yIHN5bmRyb21lIiwgIyBjdXJyZW50IGxhYmVsCiAgICAiQmFubmF5YW4tUmlsZXktUnV2YWxjYWJhIHN5bmRyb21lIiwKICAgICJCYW5uYXlhbi1ab25hbmEgc3luZHJvbWUiLAogICAgIkNvd2RlbiBzeW5kcm9tZSAxIiwKICAgICJSaWxleS1TbWl0aCBzeW5kcm9tZSIsCiAgICAiUnV2YWxjYWJhLU15aHJlLVNtaXRoIHN5bmRyb21lIiwKICAgICMgYWRkaXRpb25hbCBmcm9tIE9NSU0gKG5vdCBpbiBETykKICAgICJtdWx0aXBsZSBoYW1hcnRvbWEgc3luZHJvbWUiLAogICAgIlBURU4gaGFtYXJ0b21hIHR1bW9yIHN5bmRyb21lIHdpdGggZ3JhbnVsYXIgY2VsbCB0dW1vciIsCiAgICAibWFjcm9jZXBoYWx5LCBwc2V1ZG9wYXBpbGxlZGVtYSwgYW5kIG11bHRpcGxlIGhlbWFuZ2lvbWF0YSIsCiAgICAibWFjcm9jZXBoYWx5LCBtdWx0aXBsZSBsaXBvbWFzLCBhbmQgaGVtYW5naW9tYXRhICIsCikKCmluaXRpYWxpc20gPC0gYygiUk1TUyIsICJCWlMiLCAiUEhUUyIsICJNSEFNIiwgIkJCUlMiLCAiQ1dTMSIsICJDUyIsICJDRCIpCmBgYAoKCkJlZ2luIGJ5IHNlYXJjaGluZyBFdXJvcGVQTUMgZm9yIGFydGljbGVzIHRoYXQgY29udGFpbiBvbmUgb3IgbW9yZSBleGFjdCBtYXRjaGVzIHRvIHRoZXNlIHRlcm1zIHVzaW5nIHRoZSBkZWZhdWx0IHNlYXJjaC4gX1NhdmUgb3V0cHV0IHRvIGZpbGUsIHRvIGF2b2lkIHBvdGVudGlhbCBvZiByZXBlYXQgQVBJIGNhbGwuXwoKYGBge3J9CiMgZXhjbHVkZSBhYmJyZXZpYXRpb25zIHdoZW4gc2VhcmNoaW5nIGZvciBwdWJsaWNhdGlvbnMgKHRvbyBsaWtlbHkgdG8gCm5tX3NlYXJjaCA8LSBwYXN0ZTAoCiAgICAnIicsIHRlcm1zW3N0cmluZ3I6OnN0cl9sZW5ndGgodGVybXMpID4gNF0gLCAnIicsCiAgICBjb2xsYXBzZSA9ICIgT1IgIgopCnNlYXJjaF9zdHIgPC0gcGFzdGUwKCdPUEVOX0FDQ0VTUzp5IEFORCAoJywgbm1fc2VhcmNoLCAnKScpCgppZiAoIWZpbGUuZXhpc3RzKGRhdGFfZmlsZSkpIHsKICAgIHJlcyA8LSBldXJvcGVwbWM6OmVwbWNfc2VhcmNoKHNlYXJjaF9zdHIsIHN5bm9ueW0gPSBGQUxTRSwgbGltaXQgPSAyMDAwMCkKICAgIHJlc19pbml0IDwtIHB1cnJyOjptYXAoCiAgICAgICAgaW5pdGlhbGlzbSwKICAgICAgICBmdW5jdGlvbiguaW5pdCkgewogICAgICAgICAgICBpbml0X3NlYXJjaCA8LSBETy51dGlsczo6c2FuZHdpY2hfdGV4dCguaW5pdCwgJyInKQogICAgICAgICAgICBub19ubSA8LSBldXJvcGVwbWM6OmVwbWNfc2VhcmNoKAogICAgICAgICAgICAgICAgaW5pdF9zZWFyY2gsCiAgICAgICAgICAgICAgICBzeW5vbnltID0gRkFMU0UsCiAgICAgICAgICAgICAgICBsaW1pdCA9IDIwMDAwCiAgICAgICAgICAgICkKICAgICAgICAgICAgd19ubSA8LSBldXJvcGVwbWM6OmVwbWNfc2VhcmNoKAogICAgICAgICAgICAgICAgcGFzdGUwKGluaXRfc2VhcmNoLCAiIEFORCAoIiwgbm1fc2VhcmNoLCAiKSIpLAogICAgICAgICAgICAgICAgc3lub255bSA9IEZBTFNFLAogICAgICAgICAgICAgICAgbGltaXQgPSAyMDAwMAogICAgICAgICAgICApCiAgICAgICAgfSAKICAgICkKICAgIHNhdmUocmVzLCByZXNfaW5pdCwgZmlsZSA9IGRhdGFfZmlsZSkKfSBlbHNlIHsKICAgIGxvYWQoZGF0YV9maWxlKQp9CmBgYAoKVGhlIG51bWJlciBvZiBwdWJsaWNhdGlvbiBoaXRzIChgciBmb3JtYXQobnJvdyhyZXMpLCBiaWcubWFyayA9ICIsIilgKSBjYW4gYmUgcmVhc29uYWJseSBiZSBwcm9jZXNzZWQgdXNpbmcgYWxsIHRoZSBmdWxsIHRleHQgYXJ0aWNsZXMsIGV4Y2x1ZGluZyBwcmVwcmludHMgYW5kIHJldHJhY3Rpb25zLgoKYGBge3J9CnJlc190aWR5IDwtIHJlcyB8PgogICAgZHBseXI6OmZpbHRlcighc3RyaW5ncjo6c3RyX2RldGVjdChwdWJUeXBlLCAicmV0cmFjdHxwcmVwcmludCIpKSB8PgogICAgZHBseXI6OnNlbGVjdCgKICAgICAgICAiaWQiLCAidGl0bGUiLCAicHViWWVhciIsIHB1YkRhdGUgPSAiZmlyc3RQdWJsaWNhdGlvbkRhdGUiLCAicG1jaWQiCiAgICApIHw+CiAgICBkcGx5cjo6bXV0YXRlKAogICAgICAgIHB1YkRhdGUgPSBsdWJyaWRhdGU6OmFzX2RhdGUocHViRGF0ZSksCiAgICAgICAgcHViWWVhciA9IGx1YnJpZGF0ZTo6eWVhcihwdWJEYXRlKQogICAgKQoKaWYgKCFleGlzdHMoInJlc19mdHh0IikpIHsKICAgIHJlc19mdHh0IDwtIHJlc190aWR5IHw+CiAgICAgICAgZHBseXI6OnJvd3dpc2UoKSB8PgogICAgICAgIGRwbHlyOjptdXRhdGUoZnRfeG1sID0gZ2V0X2Z0eHRfc2FmZWx5KHBtY2lkKSkgfD4KICAgICAgICBkcGx5cjo6bXV0YXRlKGZ0ID0gcGFyc2VfZnR4dF94bWwoZnRfeG1sLCAiLy9ib2R5IikpCiAgICAKICAgIHNhdmUocmVzLCByZXNfZnR4dCwgZmlsZSA9IGRhdGFfZmlsZSkKfQpgYGAKCgojIEV2YWx1YXRpbmcgVXNhZ2UKCkV4dHJhY3RpbmcgYWxsIHRoZXNlIHZhbHVlcyBmcm9tIHRoZSBmdWxsIHRleHQgb2YgdGhlIHNhbXBsZSBwdWJsaWNhdGlvbnMgYW5kIGFsbCB0aGUgdGl0bGVzIChpbiBhIGNhc2UtaW5zZW5zaXRpdmUgbWFubmVyKS4KYGBge3J9CnJlZ2V4X3N0ciA8LSAiby5ueW9uZy5ueW9uZyggZmV2ZXIpPyIKdGVybV9kZiA8LSByZXNfdGlkeSB8PgogICAgZHBseXI6OmxlZnRfam9pbigKICAgICAgICByZXNfZnR4dCwKICAgICAgICBieSA9IGMoImlkIiwgInRpdGxlIiwgInB1YlllYXIiLCAicHViRGF0ZSIsICJwbWNpZCIpCiAgICApIHw+CiAgICBkcGx5cjo6c2VsZWN0KCJpZCIsICJwdWJEYXRlIiwgInRpdGxlIiwgImZ0IikgfD4KICAgIGRwbHlyOjptdXRhdGUoCiAgICAgICAgdGl0bGVfbWF0Y2ggPSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoCiAgICAgICAgICAgIC5kYXRhJHRpdGxlLAogICAgICAgICAgICBzdHJpbmdyOjpyZWdleChyZWdleF9zdHIsIGlnbm9yZV9jYXNlID0gVFJVRSkKICAgICAgICApLAogICAgICAgIGZ0X21hdGNoID0gc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKAogICAgICAgICAgICAuZGF0YSRmdCwKICAgICAgICAgICAgc3RyaW5ncjo6cmVnZXgocmVnZXhfc3RyLCBpZ25vcmVfY2FzZSA9IFRSVUUpCiAgICAgICAgKQogICAgKSB8PgogICAgdGlkeXI6OnVubmVzdCh0aXRsZV9tYXRjaCwga2VlcF9lbXB0eSA9IFRSVUUpIHw+CiAgICB0aWR5cjo6dW5uZXN0KGZ0X21hdGNoLCBrZWVwX2VtcHR5ID0gVFJVRSkgfD4KICAgIGRwbHlyOjptdXRhdGUoZnQgPSAhaXMubmEoZnQpKQpgYGAKClRoZSBudW1iZXIgb2YgcHVibGljYXRpb25zIHdpdGggYW5kIHdpdGhvdXQgbWF0Y2hlcyBpbiB0aGVpciB0aXRsZXMgb3IgZnVsbCB0ZXh0LCBub3Rpbmcgd2hldGhlciB0aGVpciBmdWxsLXRleHQgd2FzIG9idGFpbmVkIGFyZSBhcyBmb2xsb3dzOgoKYGBge3J9CnRlcm1fZGYgfD4KICAgIGRwbHlyOjpzdW1tYXJpemUoCiAgICAgICAgdGl0bGVfbWF0Y2ggPSBhbnkoIWlzLm5hKHRpdGxlX21hdGNoKSksCiAgICAgICAgZnRfbWF0Y2ggPSBhbnkoIWlzLm5hKGZ0X21hdGNoKSksCiAgICAgICAgZnQgPSB1bmlxdWUoZnQpLAogICAgICAgIC5ieSA9ICJpZCIKICAgICkgfD4KICAgIGRwbHlyOjpjb3VudChmdCwgdGl0bGVfbWF0Y2gsIGZ0X21hdGNoKSB8PgogICAgZHBseXI6Om11dGF0ZShwY3QgPSByb3VuZChuIC8gc3VtKG4pICogMTAwLCAyKSkgfD4KICAgIGRwbHlyOjpyZW5hbWUoZnRfb2J0YWluZWQgPSAiZnQiKQpgYGAKQW55IG5vbi1tYXRjaGVzIHdpbGwganVzdCBiZSBkcm9wcGVkIGZvciB0aGUgYW5hbHlzaXMgb2YgbmFtZXMsIGFuZCBzcGVjaWFsIHF1b3RlIG9yIGRhc2ggbWFya3Mgd2lsbCBiZSBzdGFuZGFyZGl6ZWQgdG8gYCdgLgoKYGBge3J9Cm1hdGNoZXMgPC0gdGVybV9kZiB8PgogICAgdGlkeXI6OnBpdm90X2xvbmdlcigKICAgICAgICB0aXRsZV9tYXRjaDpmdF9tYXRjaCwKICAgICAgICBuYW1lc190byA9IGMoInNvdXJjZSIsICIudmFsdWUiKSwKICAgICAgICBuYW1lc19zZXAgPSAiXyIsCiAgICAgICAgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFCiAgICApIHw+CiAgICBkcGx5cjo6bXV0YXRlKAogICAgICAgIG1hdGNoID0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKAogICAgICAgICAgICAuZGF0YSRtYXRjaCwKICAgICAgICAgICAgYygiWyfigJjigJnigLJdIiA9ICInIiwgIlst4oCQ4oCTXSIgPSAiLSIpCiAgICAgICAgKSwKICAgICAgICBtYXRjaF9sYyA9IHN0cmluZ3I6OnN0cl90b19sb3dlciguZGF0YSRtYXRjaCkKICAgICkgfD4KICAgIGRwbHlyOjpzZWxlY3QoLSJ0aXRsZSIpCmBgYAoKVGhlIG51bWJlciBvZiBjYXNlLWluc2VuaXRpdmUgbWF0Y2hlcyBpbiB0aGUgdGl0bGVzIGFuZCBmdWxsIHRleHQgYXJlIGFzIGZvbGxvd3M6CmBgYHtyfQptYXRjaGVzIHw+CiAgICBETy51dGlsczo6Y29sbGFwc2VfY29sKCJtYXRjaCIpIHw+CiAgICBkcGx5cjo6Y291bnQoLmRhdGEkc291cmNlLCAuZGF0YSRtYXRjaF9sYykKYGBgCgpUaGUgbnVtYmVyIG9mIG1hdGNoZXMsIGluIHRoZSBmdWxsIHRleHQgb25seSwgcHJlc2VydmluZyBjYXNlIGFyZSBhcyBmb2xsb3dzOgpgYGB7cn0KZnRfbWF0Y2hlcyA8LSBtYXRjaGVzIHw+CiAgICBkcGx5cjo6ZmlsdGVyKC5kYXRhJHNvdXJjZSA9PSAiZnQiKQoKZnRfbWF0Y2hlcyB8PgogICAgRE8udXRpbHM6OmNvbGxhcHNlX2NvbCgibWF0Y2hfbGMiKSB8PiAKICAgIGRwbHlyOjpjb3VudCguZGF0YSRtYXRjaCwgc29ydCA9IFRSVUUpCmBgYAoKVGhlIGN1cnJlbnQgbmFtZSBpbiBETyBpcyBwcmV0dHkgbG93IGluIHRoZSBsaXN0IGFuZCBkb2Vzbid0IG1hdGNoIHRoZSBvcmlnaW5hbC4gVGhlIHRvcCB0d28gYXJlIHRoZSBvcmlnaW5hbCwgd2l0aCB0aGUgc2Vjb25kIGJlaW5nIHRoZSBvcmlnaW5hbCBjYXBpdGFsaXphdGlvbi4gVGhlIHVwcGVyY2FzZSB2ZXJzaW9uIGlzIG11Y2ggbW9yZSBjb21tb24uIEl0J3MgbXVjaCBsZXNzIGNvbW1vbiB0byBmaW5kIHRoZSBuYW1lIHdpdGggImZldmVyIiBidXQgdGhhdCdzIHByb2JhYmx5IHRvIGJlIGV4cGVjdGVkIHNpbmNlIHRoZSBuYW1lIG9mIHRoZSB2aXJ1cyB3aWxsIGFsbW9zdCBjZXJ0YWlubHkgYmUgdXNlZCBhdCBsZWFzdCBvbmNlIHdpdGggZWFjaCBkaXNlYXNlIHJlZmVyZW5jZSwgYW5kIG9mdGVuIG11Y2ggbW9yZS4KCk9yZ2FuaXplZCBieSBwdWJsaWNhdGlvbiBkYXRlIGFuZCBiaW5uZWQgaW50byB5ZWFyIGludGVydmFscyAobGltaXRlZCB0byB0aGUgdG9wIHRlbiksIHRoZSByZXN1bHRzIGFyZSBhcyBmb2xsb3dzOgoKYGBge3J9CmdfY29sb3JzIDwtIGh1ZXM6Oml3YW50aHVlKGRwbHlyOjpuX2Rpc3RpbmN0KGZ0X21hdGNoZXMkbWF0Y2gpKQoKZnRfbWF0Y2hlcyB8PgogICAgRE8udXRpbHM6OmNvbGxhcHNlX2NvbCgibWF0Y2hfbGMiKSB8PgogICAgZHBseXI6Om11dGF0ZShuID0gbGVuZ3RoKC5kYXRhJHNvdXJjZSksIC5ieSA9ICJtYXRjaCIpIHw+CiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2ZyZXFwb2x5KAogICAgICAgIGdncGxvdDI6OmFlcyh4ID0gcHViRGF0ZSwgY29sb3IgPSBtYXRjaCksCiAgICAgICAgYmlud2lkdGggPSAzNjUKICAgICkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdfY29sb3JzKSArCiAgICBnZ3Bsb3QyOjpmYWNldF93cmFwKH4gc291cmNlLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpCmBgYAoKSG1tLi4uIHRoZSBvbGRlc3QgdXNlcyBhcmUgcXVpdGUgYSBsb25nIHRpbWUgYWdvIGFuZCBtYWtlIHRoZSBncmFwaCBhIGJpdCBoYXJkIHRvIHJlYWQuIFN1YnNldHRpbmcgdGhlIGdyYXBoIHRvIGFmdGVyIHRoZSB5ZWFyIDIwMDA6CmBgYHtyIHdhcm5pbmc9RkFMU0V9CmcgPC0gZnRfbWF0Y2hlcyB8PgogICAgRE8udXRpbHM6OmNvbGxhcHNlX2NvbCgibWF0Y2hfbGMiKSB8PgogICAgZHBseXI6Om11dGF0ZShuID0gbGVuZ3RoKC5kYXRhJHNvdXJjZSksIC5ieSA9ICJtYXRjaCIpIHw+CiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2ZyZXFwb2x5KAogICAgICAgIGdncGxvdDI6OmFlcyh4ID0gcHViRGF0ZSwgY29sb3IgPSBtYXRjaCwgZ3JvdXAgPSBtYXRjaCksCiAgICAgICAgYmlud2lkdGggPSAzNjUsCiAgICAgICAgbGluZXdpZHRoID0gMQogICAgKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ19jb2xvcnMpICsKICAgIGdncGxvdDI6OnNjYWxlX3hfZGF0ZSgKICAgICAgICBkYXRlX2JyZWFrcyA9ICI1IHllYXJzIiwKICAgICAgICBkYXRlX2xhYmVscyA9ICIlWSIKICAgICkgKwogICAgZ2dwbG90Mjo6ZmFjZXRfd3JhcCh+IHNvdXJjZSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICBnZ3Bsb3QyOjpjb29yZF9jYXJ0ZXNpYW4oCiAgICAgICAgeGxpbSA9IGMoYXMuRGF0ZSgiMjAwMC0wMS0wMSIpLCBhcy5EYXRlKCIyMDI1LTAxLTAxIikpCiAgICApICsKICAgIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShnKQpgYGAKCkJhc2VkIG9uIHRoaXMsIGl0J3MgdmVyeSBjbGVhciB0aGF0IHRoZSBjYXBpdGFsaXplZCB2ZXJzaW9uIG9mIHRoZSBvcmlnaW5hbCBuYW1lIGlzIG1vc3QgY29tbW9uLiBKdXN0IGxvb2tpbmcgYXQgdGhlIGRpc2Vhc2UgbmFtZSBqdXN0IHRvIGJlIHN1cmUgdGhpcyBpcyBhbHNvIHRoZSBjYXNlIGZvciB0aGUgZGlzZWFzZToKYGBge3Igd2FybmluZz1GQUxTRX0KZyA8LSBmdF9tYXRjaGVzIHw+CiAgICBkcGx5cjo6ZmlsdGVyKHN0cmluZ3I6OnN0cl9kZXRlY3QoLmRhdGEkbWF0Y2gsICJldmVyIikpIHw+CiAgICBETy51dGlsczo6Y29sbGFwc2VfY29sKCJtYXRjaF9sYyIpIHw+CiAgICBkcGx5cjo6bXV0YXRlKG4gPSBsZW5ndGgoLmRhdGEkc291cmNlKSwgLmJ5ID0gIm1hdGNoIikgfD4KICAgIGdncGxvdDI6OmdncGxvdCgpICsKICAgIGdncGxvdDI6Omdlb21fZnJlcXBvbHkoCiAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBwdWJEYXRlLCBjb2xvciA9IG1hdGNoLCBncm91cCA9IG1hdGNoKSwKICAgICAgICBiaW53aWR0aCA9IDM2NSwKICAgICAgICBsaW5ld2lkdGggPSAxCiAgICApICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBnX2NvbG9ycykgKwogICAgZ2dwbG90Mjo6c2NhbGVfeF9kYXRlKAogICAgICAgIGRhdGVfYnJlYWtzID0gIjUgeWVhcnMiLAogICAgICAgIGRhdGVfbGFiZWxzID0gIiVZIgogICAgKSArCiAgICBnZ3Bsb3QyOjpmYWNldF93cmFwKH4gc291cmNlLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShnKQpgYGAKCkEgZ2VuZXJhbCBzdW0gd291bGQgYmUgbW9yZSB1c2VmdWwgYnV0IHRoZSBkaWZmZXJlbmNlcyB0aGVyZSBhcmUgbmVnbGlnaWJsZS4gSW4gdGhpcyBjYXNlLCBpdCBtYWtlcyBzZW5zZSB0byBmb2xsb3cgdGhlIG1vc3QgY29tbW9uIG5hbWUgZm9yIHRoZSB2aXJ1cy4K